package com.sloan.community.config;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.sloan.community.common.Constant;
import com.sloan.community.util.SnowflakeIdGenerator;
import com.sloan.community.ws.CommunityWebSocketHandler;
import lombok.SneakyThrows;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import org.springframework.web.server.WebSession;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import org.springframework.web.server.session.DefaultWebSessionManager;
import org.springframework.web.server.session.InMemoryWebSessionStore;
import org.springframework.web.server.session.WebSessionIdResolver;
import org.springframework.web.server.session.WebSessionManager;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class WebFluxConfig implements WebFluxConfigurer {


    @Bean
    public WebClient webClient() {
        return WebClient.builder()
                .build();
    }

    @Bean("ossClient")
    @SneakyThrows
    public OSS ossClient(AliyunOssProperties properties) {
        DefaultCredentialProvider defaultCredentialProvider =
                CredentialsProviderFactory.newDefaultCredentialProvider(
                        properties.getAccessKey(), properties.getAccessSecret());

        return OSSClientBuilder.create()
                .endpoint(properties.getEndpoint())
                .credentialsProvider(defaultCredentialProvider)
                .region(properties.getRegion())
                .build();
    }

    @Bean
    public SnowflakeIdGenerator generator() {
        return new SnowflakeIdGenerator(1);
    }

    @Bean(name = WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME)
    public WebSessionManager webSessionManager(ObjectProvider<WebSessionIdResolver> webSessionIdResolver,
                                               ServerProperties serverProperties, DefaultBeforeConvertCallBack convertCallback) {
        DefaultWebSessionManager webSessionManager = new DefaultWebSessionManager();
        Duration timeout = serverProperties.getReactive().getSession().getTimeout();
        int maxSessions = serverProperties.getReactive().getSession().getMaxSessions();

        InMemoryWebSessionStore store = new InMemoryWebSessionStore() {
            @Override
            public Mono<WebSession> createWebSession() {
                return super.createWebSession().doOnSuccess(session -> {
                    session.setMaxIdleTime(timeout);
                    // 注入session
                    convertCallback.setSession(session);
                });
            }
        };
        store.setMaxSessions(maxSessions);
        webSessionManager.setSessionStore(store);
        webSessionIdResolver.ifAvailable(webSessionManager::setSessionIdResolver);
        return webSessionManager;
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("PUT", "DELETE", "GET", "POST", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("*")
                .allowCredentials(false)
                .maxAge(3600);
    }

    @Bean
    public HandlerMapping handlerMapping(CommunityWebSocketHandler webSocketHandler) {
        Map<String, WebSocketHandler> map = new HashMap<>(1);
        map.put(Constant.NoticeUrlPattern.PATTERN, webSocketHandler);
        return new SimpleUrlHandlerMapping(map, Ordered.HIGHEST_PRECEDENCE);
    }

    @Bean
    public WebSocketHandlerAdapter handlerAdapter() {
        return new WebSocketHandlerAdapter(new HandshakeWebSocketService());
    }

}
